import React from "react";
import AbstractDataGrid from "./AbstractDataGrid";
import {findIndex, forEach, forIn, isArray, isEmpty, isFunction, isUndefined} from "lodash-es";
import {getRandomKey, isNotEmpty} from "jh-utils";
import {FieldCtrl} from "../Config";

/**
 * 表格组件的基类。
 * 用于定义表格组件通用的 属性 和 方法；
 */
export default class AbstractEditDataGrid extends AbstractDataGrid {

    /**
     * 设置表格的操作权限
     * @param viewConfig {{
     *     body: {
              "fieldName": string,
              "addable": boolean,
              "deletable": boolean,
              "editable": boolean,
              "standard": boolean
            },
            fields: [{
              "fieldName": string,
              "control": number
            }]
     * }}
     * body 的结构：{
              "fieldName": "securityGroups",
              "addable": false, // 是否可添加
              "deletable": false, // 是否可删除
              "editable": false, // 是否可编辑
              "standard": false // 是否标准表体
            }
     * fields 的结构：[{
              "fieldName": "roles.corp", // 字段名
              "control": 2 // 对应 Config/FieldCtrl
            }]
     * @param afterCb
     */
    setViewConfig(viewConfig, afterCb) {
        afterCb && afterCb();
    }

    fieldCtrlObj = {};
    fieldEnabledObj = {};

    /**
     * 设置表体的操作权限
     * @param fields {[{
              "fieldName": string,
              "control": number
            }]}
     * @param freshRender {boolean} 是否需要刷新渲染组件。默认 false
     * @param afterCb
     */
    setFieldCtrlByFields(fields, freshRender = true, afterCb) {

        if (!isEmpty(fields)) {
            forEach(fields, (fieldConfig) => {
                this.fieldCtrlObj[fieldConfig.fieldName] = fieldConfig.control;
            });

            if (freshRender) {
                this.setState({randomKey: getRandomKey()}, afterCb);
                return;
            }
        }
        afterCb && afterCb();
    }

    /**
     * 设置表格字段是否可用
     * @param fieldName {string}
     * @param enabled {boolean|function:boolean}
     * @param freshRender {boolean}
     * @param afterCb {function}
     */
    setFieldEnabled(fieldName, enabled, freshRender, afterCb) {

        if (fieldName) {
            this.fieldEnabledObj[fieldName] = enabled;

            if (freshRender) {
                this.setState({randomKey: getRandomKey()}, afterCb);
                return;
            }
        }
        afterCb && afterCb();
    }

    /**
     * 设置头部按钮是否可用
     * @param actions {string[]}
     * @param enabled {boolean}
     * @param afterCb
     */
    setActionEnabled(actions, enabled, afterCb) {
    }

    setSelectedKeys(selectedRowKeys, afterCb) {

        if (this.tableRef && isFunction(this.tableRef.setSelectedKeys)) {
            this.tableRef.setSelectedKeys(selectedRowKeys, afterCb);
        }
    }

    /**
     * 聚焦到某个单元格
     * @param rowCol {{row: string, col: string}} 聚焦的行列。row：行的 key。col： 列的 dataIndex
     * @param editing {boolean} 是否正在编辑
     * @param afterCb
     */
    setFocusCell(rowCol, editing, afterCb) {

        if (this.tableRef && isFunction(this.tableRef.setFocusCell)) {
            this.tableRef.setFocusCell(rowCol, editing, afterCb);
        }
    }

    /**
     * 重新渲染表格，以重新计算表格的高度
     * @param cb
     */
    reRenderTable(cb) {
        this.tableRef && this.tableRef.reRenderTable(cb);
    }

    /**
     * 字段的校验回调函数
     * @type {{[fieldName]: [(function(fieldValue: *):{isOk: bool, error: string})]}}
     */
    fieldVerifyCbObj = {};

    /**
     *
     * @param fieldName
     * @param cbs {[(function(fieldValue: *):{isOk: bool, error: string})]}
     */
    setFieldVerifyCbs(fieldName, cbs) {

        if (fieldName) {
            this.fieldVerifyCbObj[fieldName] = cbs;
        }
    }

    /**
     * 将 data 数据排到前面，空行移到后面
     * @param data {[object]} 需要排到前面的数据列表
     * @param afterCb
     */
    refreshWithEmptyRow(data, afterCb) {
        if (this.tableRef && isFunction(this.tableRef.refreshWithEmptyRow)) {
            return this.tableRef.refreshWithEmptyRow(data, afterCb);
        }
    }

    /**
     * 重置“用于标识 表格有数据变化”的变量
     */
    resetModify() {

        if (this.tableRef && isFunction(this.tableRef.resetModify)) {
            return this.tableRef.resetModify();
        }
    }

    /**
     * 行操作
     * @param actKey {string} 对应 TableBarActions 的值
     */
    editRow(actKey) {

        if (this.tableRef && isFunction(this.tableRef.editRow)) {
            this.tableRef.editRow(actKey);
        }
    }

    /**
     * 获取必填的字段。数据结构：{
     *     [fieldName]: {fieldName: 'string 字段名', cn: 'string 中文描述', en: 'string 英文描述'}
     * }
     * @return {{}}
     */
    getRequiredFields() {
        const colsObj = this.getColFields();
        const fields = {};

        forIn(this.fieldCtrlObj, (ctrl, fieldName) => {
            if (ctrl === FieldCtrl.REQUIRED) {// 必填
                fields[fieldName] = colsObj[fieldName] ? colsObj[fieldName] : {
                    fieldName, cn: '', en: ''
                }
            }
        });
        return fields;
    }

    getColFields() {
        const {colsConf} = this.props;
        const colsObj = {};

        forEach(colsConf, (col) => {
            if (col && col.name) {
                colsObj[col.name] = {
                    fieldName: col.name,
                    cn: col.remark,
                    en: col.remark
                };
            }
        });
        return colsObj;
    }

    /**
     * 校验每行的必填字段
     * （在 afc 的 getAjaxData4Save 方法调用）
     * @param onlyChange {boolean} 是否只校验“有数据变化”的记录
     * @param customRender {function({fObj, record, line, fieldName})} 自定义错误信息的渲染方法
     * @param data {[]} 如果传入这个参数，则以这个参数的值作为校验的数据源。否则默认取当前表格的数据进行校验
     * @param local
     * @return {{data: [], errors: []}}
     */
    verifyRequired(onlyChange = true, {customRender, data, local}) {
        let isByData = !isUndefined(data);
        const dataList = isByData ? data : this.getDataNoEmpty(onlyChange);
        const errors = [];
        const requiredFields = this.getRequiredFields();

        if (!isEmpty(requiredFields) || !isEmpty(this.fieldVerifyCbObj)) {
            const allDataList = isByData ? data : (onlyChange ? this.getDataNoEmpty(false) : dataList);

            forEach(allDataList, (record, ind) => {
                if (record) {
                    let line = ind + 1;
                    forIn(requiredFields, (fObj, fieldName) => {
                        if (!isNotEmpty(record[fieldName])) {
                            // if (onlyChange) {
                            //     let tmpInd = findIndex(allDataList, (v) => (v && v.key === record.key));
                            //     if (tmpInd > -1) line = tmpInd + 1;
                            // }

                            if (isFunction(customRender)) {
                                errors.push(customRender({fObj, line, fieldName}));
                            }
                        }
                    });

                    forIn(this.fieldVerifyCbObj, (cbs, fieldName) => {
                        forEach(cbs, (cb) => {
                            if (isFunction(cb)) {
                                let reObj = cb(record[fieldName], record);
                                if (reObj) {
                                    let {error, isOk} = reObj;
                                    if (false === isOk) {
                                        // if (onlyChange) {
                                        //     let tmpInd = findIndex(allDataList, (v) => (v && v.key === record.key));
                                        //     if (tmpInd > -1) line = tmpInd + 1;
                                        // }
                                        if (isFunction(customRender)) {
                                            errors.push(customRender({
                                                line,
                                                fieldName,
                                                desc: error
                                            }));
                                        } else {
                                            errors.push(error);
                                        }
                                    }
                                }
                            }
                        })
                    })
                }
            });
        }

        return {data: dataList, errors};
    }

    /**
     * “单元格-编辑前”的回调
     * @type {{}}
     */
    editBeforeCbs = {};

    /**
     * “单元格-编辑后”的回调
     * @type {{}}
     */
    editAfterCbs = {};

    /**
     * 设置“字段单元格-编辑前”事件处理。重复调用此方法，可实现“多次事件绑定”
     * @param fieldName {string}
     * @param func {function(compIns)} // compIns 是当前编辑组件的实例
     */
    addEditBeforeHandler(fieldName, func) {
        if (!isArray(this.editBeforeCbs[fieldName])) {
            this.editBeforeCbs[fieldName] = [];
        }
        this.editBeforeCbs[fieldName].push(func);
        // console.log('addEditBeforeHandler')
    }

    /**
     * 设置“字段单元格-编辑后”事件处理。重复调用此方法，可实现“多次事件绑定”
     * @param fieldName {string}
     * @param func {function(value, record)}
     */
    addEditAfterHandler(fieldName, func) {
        if (!isArray(this.editAfterCbs[fieldName])) {
            this.editAfterCbs[fieldName] = [];
        }
        this.editAfterCbs[fieldName].push(func);
    }

    /**
     * @type {function | null}
     */
    rowActionCb = null;

    /**
     * 设置“行操作”时的回调。例如：添加/删除/移动 行
     * @param func {function({type,record,rowIndex,dataList,preDataList})}
     */
    setRowActionHandler(func) {
        this.rowActionCb = func;
    }

    /**
     * 设置“默认行数据”
     * @param func {function(currentRecord: object|null):object} 此函数返回的对象，将作为子表的默认行数据
     */
    setDefaultRecord(func) {
        this.setCompProps({getDefaultRecord: func});
    }
}
